package upm.pb4agent.team3;

import jade.content.Concept;
import jade.content.ContentElement;
import jade.content.lang.Codec;
import jade.content.lang.Codec.CodecException;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.content.onto.OntologyException;
import jade.content.onto.basic.Action;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.SimpleBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import upm.pb4agent.ontology.PB4AgentOntology;
import upm.pb4agent.ontology.PickSeat;
import upm.pb4agent.ontology.RealRow;
import es.decoroso.communicationAgent.bean.Message;
import es.decoroso.communicationAgent.bean.Rower;
import es.decoroso.communicationAgent.bean.Vector2;
import es.decoroso.fakeAgent.behaviours.NotifyGraphicLayer;

/**
 * This agent has the following functionality: 
 * <ul>
 * <li> 
 * <li> 
 * </ul>
 * @author Team3, UPM
 */
public class Boat extends Agent{//es.decoroso.fakeAgent.Boat{
	private static final long serialVersionUID = 1496478826044333646L;
	
	//public List pickedSeatsList = new ArrayList(Constant.MAX_SEATS);
	private Codec codec = new SLCodec();
	private Ontology ontology = PB4AgentOntology.getInstance();
	
	private Map<String, AID> mapSeatRowerAID = new HashMap<String, AID>();
	int x;
	int y;
	
	////////////////////////////////////////////////
	////  ADD ALL THIS PARAMS 					////
	////////////////////////////////////////////////
		/** Actual position of the boat */
		Vector2 position = new Vector2(0f, 0f);
		
		/** Actual orientation of the boat */
		Vector2 orientation = new Vector2(0f, 0f);
		
		/** Group number */ // Put your group number here
		public int groupNumber = Constant.TEAM_NUMBER;
		
		/** Rowers of the boat */ // You have to update this variable
		public List<es.decoroso.communicationAgent.bean.Rower> rowers = new LinkedList<es.decoroso.communicationAgent.bean.Rower>();
		
		// Params used to calculate the row 
		float timer = 3000f;
		float posX, posY;
		float rowX, rowY;
		
		/** Id of the communication agent */
		private AID comunicationAgent;
	////////////////////////////////////////////////
	////  ADD ALL THIS PARAMS 					////
	////////////////////////////////////////////////
	
	public Boat(){
		super();
	}
	
	protected void setup(){
		try{
			
			System.out.println(getLocalName() + ": enters the system");			
						
			getContentManager().registerLanguage(codec);
			getContentManager().registerOntology(ontology);
			
			Object[] args = getArguments();
			if (args != null) {
				this.groupNumber = Integer.parseInt((String)args[0]);
			}else{
				this.groupNumber = Constant.TEAM_NUMBER;
			}
			//System.out.println("this.groupNumber " + this.groupNumber );
						
			/**
			* On the setup method we should find the COMMUNICATION agent, and add the
			* new behaviour.
			* 
			* You have to merge this setup with yours
			*/
			rowers.add(new Rower(3, 1));
				groupNumber = Constant.TEAM_NUMBER;
				//super.setup();
				
				doWait(2000); // Wait for the communication service to be ready
				
				// SET THE START POSITION
				setStartPosition();
	
				// FIND THE COMMUNICATION AGENT
				this.comunicationAgent = locateService(this, "COMMUNICATION"); // ADD THIS  LINE
				
				//
				ReadConfiguration.getInstance(1);
				String propTmp = ReadConfiguration.getValue("start_gui");
				boolean startGui = true;
				if(propTmp != null){
					startGui = Boolean.parseBoolean(propTmp);					
				}				
				if(startGui){
					if(this.comunicationAgent != null){
						// ADD THE BEHAVIOUR
						addBehaviour(new es.decoroso.fakeAgent.behaviours.NotifyGraphicLayer(this,comunicationAgent)); // ADD THIS LINE
					}else{
						addBehaviour(new LocalizeComunicatorAgentGUI(this));
					}
				}				
				
			/**
			* On the setup method we should find the COMMUNICATION agent, and add the
			* new behaviour.
			* 
			* You have to merge this setup with yours
			*/
				addBehaviour(new ProcessingSeatPositionFromRower(this));
				addBehaviour(new ProcesingRowFromBoat(this));
		}catch(Exception e){
			System.out.println(getLocalName() + "-::-"  + e.toString());
		}
	}
	
	class ProcessingSeatPositionFromRower extends SimpleBehaviour {
		private static final long serialVersionUID = -925893978546868118L;
		boolean end = false;
	
		public ProcessingSeatPositionFromRower(Agent agent) {
			super(agent);
		}	
		
		public void action(){ 
			try{
	
				ACLMessage msg = receive(MessageTemplate.and(
															 MessageTemplate.and(
																				 MessageTemplate.and(
																									 MessageTemplate.MatchLanguage(codec.getName())
																									 ,MessageTemplate.MatchOntology(ontology.getName())
																									 )
																				,MessageTemplate.MatchPerformative(ACLMessage.INFORM))
															 ,MessageTemplate.MatchProtocol("PickSeat")
															)
										 );
				//System.out.println(getLocalName() + ": Before to know seat taked");
				if(msg != null){
					ContentElement contentElement = super.myAgent.getContentManager().extractContent(msg);					
					if (contentElement instanceof Action) {
						Action action = (Action) contentElement;
						Concept concept = action.getAction();
						if(concept instanceof PickSeat) {
							PickSeat pickedSeat = (PickSeat) concept;
					        String seatID = pickedSeat.getSeatID().getSeatID();
					        AID rowerAID = msg.getSender();
					        String availableSeats = pickingSeat(seatID, rowerAID);
					        
					        if(availableSeats.equals("")){
					        	ACLMessage reply = new ACLMessage(ACLMessage.INFORM);
					        	reply.addReceiver(msg.getSender());
								reply.setLanguage(codec.getName());
								reply.setOntology(ontology.getName());		    		    
						    	reply.setProtocol("SeatChoosed");
						    	send(reply);
						    	
						    	int rowerNumber = Integer.parseInt(rowerAID.getLocalName().substring(rowerAID.getLocalName().length() - 1,rowerAID.getLocalName().length()));
								int seatNumber = Integer.parseInt(seatID);
						    	es.decoroso.communicationAgent.bean.Rower rower = new es.decoroso.communicationAgent.bean.Rower(rowerNumber,seatNumber);
						    	//rowers.add(rower);
						    	System.out.println("GUI--------->Rower " + rowerNumber + "Asiento:" + seatNumber);
						    	addRower(rowerAID.getLocalName(), seatID);
					        }else{
					        	ACLMessage reply = new ACLMessage(ACLMessage.REFUSE);
					        	reply.addReceiver(msg.getSender());	
								reply.setLanguage(codec.getName());
								reply.setOntology(ontology.getName());		    		    
						    	reply.setProtocol("SeatChoosed");
						    	send(reply);
					        }
						}
					}
				}else{
					block();
				}
						
			}catch (CodecException ce){
				System.out.println(getLocalName() + ", error: "+ ce.toString());
				//end = true;
			}catch (OntologyException oe){
				System.out.println(getLocalName() + ", error: "+ oe.toString());
				//end = true;
			}catch (Exception e) {
				System.out.println(getLocalName() + ", error: "+ e.toString());
				//end = true;
			}
		}	
		//End: action
		public boolean done(){
	    	return false;
	    }
				
	}
	
	private String pickingSeat(String seatID, AID rowerAID){
		//Object seat = mapSeatRowerAID.get(seatID);				
	    
	    if (!mapSeatRowerAID.containsKey(rowerAID)) {
	    	mapSeatRowerAID.remove(seatID);
	    	mapSeatRowerAID.put(seatID, rowerAID);
	    	System.out.println(getLocalName() + " <-- SeatID " + seatID + " assigned to " + rowerAID.getLocalName());
	    	return "";
	    }else{
	    	/*AID rowerAIDUsed = (AID)mapSeatRowerAID.get(seatID);
	    	System.out.println(getLocalName() + " <-- Seat was assigned before to: " + rowerAIDUsed.getLocalName());
	    	String availableSeats="";
	    	
	    	Collection rowerAID
	    	for(int i=0; i<mapSeatRowerAID.size();i++){
	    		Object obj = mapSeamapSeatRowerAIDtRowerAID.values()(i);
	    		if(obj == null){
	    			availableSeats = String.valueOf(i);
	    		}
	    	}
	    	//Object obj = mapSeatRowerAID.containsValue(null);
	    	System.out.println("availableSeats: " + availableSeats);
		    return availableSeats;*/
		    return "";
	    }
	    
	}
	
	
	class LocalizeComunicatorAgentGUI extends SimpleBehaviour {
		private static final long serialVersionUID = -925893978546868118L;
		boolean encontrado = false;
		
		public LocalizeComunicatorAgentGUI(upm.pb4agent.team3.Boat agentBoat) {
			super(agentBoat);
		}	
		
		public void action(){ 
			try{
				// FIND THE COMMUNICATION AGENT
				comunicationAgent = locateService(myAgent, "COMMUNICATION"); // ADD THIS  LINE
				
				if(comunicationAgent != null){
					// ADD THE BEHAVIOUR
					addBehaviour(new es.decoroso.fakeAgent.behaviours.NotifyGraphicLayer((upm.pb4agent.team3.Boat)myAgent ,comunicationAgent)); // ADD THIS LINE
					encontrado = true;
				}else{
					encontrado = false;
				}
				
			}catch (Exception ce){
				System.out.println(getLocalName() + ", error: "+ ce.toString());
			}
		}	
		//End: action
		public boolean done(){
	    	return encontrado;
	    }
				
	}
	
class ProcesingRowFromBoat extends SimpleBehaviour{

	private static final long serialVersionUID = -5037580285567696937L;
	public ProcesingRowFromBoat(Agent agent){
		super();
	}
	
	public void action(){		
		List<Integer> performativeList = new ArrayList<Integer>(1);
		performativeList.add(ACLMessage.INFORM);
		List<String> protocolList = new ArrayList<String>(1);
		protocolList.add("RealRow");
		MessageTemplate mt = MyAgent.addMessageTemplatOR(performativeList, protocolList);				
		ACLMessage msg =  receive(mt);
		//System.out.println(getLocalName() + ", Boat <<<--: "+ msg);
		
		if (msg != null){
			try{
				ContentElement contentElement = getContentManager().extractContent(msg);
				
				if (!(contentElement instanceof Action)) return;
				
				Action action = (Action)contentElement;
				Concept concept = action.getAction();
				
				if (!(concept instanceof RealRow)) return;
				
				RealRow realRow = (RealRow)concept;				
				
				x = realRow.getX();
				y = realRow.getY();
				toRow(x, y);System.out.println("GUI--------->X " + x + "Y:" + y);
			}catch (Exception e){
				e.printStackTrace();
				System.out.println(getLocalName() + ", error: "+ e.toString());
			}			
		}else{
			block();
		}
	}
	public boolean done(){
		return false;
	}
}
	
public void addRower(String rowerName, String seatID){
  int rowerId = Character.getNumericValue(rowerName.charAt(rowerName.length() - 1));
  boolean found = false;

  for (Rower rower : this.rowers) {
    if (rower.getNumber() == rowerId) {
      found = true;
      rower.setPlace(Integer.parseInt(seatID));
      break;
    }
  }

  if (!found)
    this.rowers.add(new Rower(rowerId, Integer.parseInt(seatID)));
}

	///////////////////////////////////////////////////////
	// ADD THE CODE FROM HERE TO THE END OF THE DOCUMENT //
	///////////////////////////////////////////////////////
	/**
	* Call this method after set the group number
	* 
	* This methods sets the start position of the boat
	*/
	public void setStartPosition() {
		position.x = groupNumber*25;	
	}
	/**
	* Start a row
	* 
	* This method should be called when a new row it's been received.
	* 
	* @param x X Value of the row
	* @param y Y Value of the row
	*/
	public void toRow(float x, float y) {
		posX = position.x;
		posY = position.y;
		
		if (timer < 50000) { // row not finished
			float restX =  orientation.x - orientation.x * timer / 3000.0f;
			float restY =  orientation.y - orientation.y * timer / 3000.0f;
			
			orientation.x = restX + x;
			orientation.y = restY + y;
		} else {
			orientation.x = x;
			orientation.y = y;
		}//System.out.println("---------------->x: " + x + "," + " y" + y);
		
		timer = 0;
	}

	
	/**
	* Update the position of the boat
	*/
	private void updateRow() {
		if (timer <= 3000) {
			position.x = posX + (orientation.x * timer / 3000.0f);
			position.y = posY + (orientation.y * timer / 3000.0f);
		}
	}

	/**
	* This method is called from NotifyGraphicLayer
	* 
	* This method updates the row force and send the information
	* to the graphic layer
	* 
	* @return Message with the actual position of the system
	*/
	public Message createMessage() {
		timer = timer + 50;
		updateRow();
		Message m = new Message(groupNumber, orientation, position, rowers);
		return m;
	}

	/**
	* This method is called from NotifyGraphicLayer
	* 
	* This method updates the row force and send the information
	* to the graphic layer
	* 
	* @return Message with the actual position of the system
	*/
	public Message createMessageRandom() {
		timer = timer + 50;
		float xForce = 3;
		if ( Math.random() > 0.5) {
			xForce *= -1;
		}
		float yForce = (float)(5 * Math.random());
		if (timer >= 2000) {
			toRow(xForce, yForce);
		}
		updateRow();
		rowers.clear();
		rowers.add(new Rower(2, 3));
		
		Message m = new Message(groupNumber, orientation, position, rowers);
		//System.out.println("Boat Graphic Layer");
		return m;
	}

	/**
	* This method search for an agent that offers a service we are searching
	* for
	* 
	* @param agentType Type of agent you want to locate
	* 
	* @return AID of identifyed agent or null if not found
	*/
	private AID locateService(Agent agent, String agentType) {
		//System.out.println("LOCALIZANDO COMUNICADOR:: " + agentType);
		AID result = null;
		
		// Create agent description
		DFAgentDescription dfd = new DFAgentDescription();
		ServiceDescription sd = new ServiceDescription();
		sd.setType(agentType);
		dfd.addServices(sd);
		
		// Search requested type agents
		DFAgentDescription[] res = new DFAgentDescription[1];
		try {
			
			res = DFService.search(agent, dfd);
			if (res.length > 0) {
				result = res[0].getName();//System.out.println("SI LOCALIZADO COMUNICADOR:: " + agentType);
			} else {
				// Not agent found
				//System.out.println("NO LOCALIZADO COMUNICADOR:: " + agentType);
				return null;
			}
		} catch (FIPAException ex) {
			// Not agent found exception
			ex.printStackTrace();
		}
		return result;
	}
	///////////////////////////////////////////////////////
	// ADD THE CODE FROM HERE TO THE END OF THE DOCUMENT //
	///////////////////////////////////////////////////////
	
	
}